﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace per.esmool.utils {

    public class RandKit {

        public RandKit() { }

        readonly Random random = new();

        public T Sample<T>(List<T> list) {
            var len = list.Count;
            var r = this.random.Next(len);
            return list[r];
        }

        public int SampleValue(int start, int end) {
            return this.random.Next(start, end);
        }

        public T Sample<T>(T inRangeValue, T offRangeValue, double rate = 0.05) {
            double r = random.NextDouble();
            return r < rate ? inRangeValue : offRangeValue;
        }

        public T Sample<T>(List<Tuple<T, double>> options) {
            var list = this.StandarizedSampleOptions(options);
            double r = random.NextDouble();
            foreach (var t in list) {
                if (r < t.Item2)
                    return t.Item1;
            }
            return default;
        }

        private List<Tuple<T, double>> StandarizedSampleOptions<T>(List<Tuple<T, double>> options) {
            var list = new List<Tuple<T, double>>(options);
            list.Sort((x, y) => Math.Sign(x.Item2 - y.Item2));
            var firstLargeThenOne = list.Where(x => x.Item2 >= 1).FirstOrDefault();
            var qs = list.Where(x => x.Item2 < 1);
            if (firstLargeThenOne is not null)
                qs = qs.Concat([firstLargeThenOne]);
            return qs.ToList();
        }

        public Func<string, string> GetRandomTextInterpoletor() {
            return (string expression) => {
                var parts = expression.Split('|');
                var list = this.StarndarizSampleOptions(parts);
                return this.Sample(list);
            };
        }

        private List<Tuple<string, double>> StarndarizSampleOptions(string[] parts) {
            var rateAssigned = new List<Tuple<string, double>>();
            var rateUnassigned = new List<Tuple<string, double>>();
            foreach (var part in parts) {
                if (!part.Contains(',')) {
                    var spans = part.Split(',').Select(x => x.Trim()).ToArray();
                    var text = spans[0];
                    var rate = double.TryParse(spans[1], out var result) ? result : 1d;
                    rateAssigned.Add(Tuple.Create(text, rate));
                } else {
                    var text = part.Trim();
                    var rate = 1d;
                    rateUnassigned.Add(Tuple.Create(text, rate));
                }
            }

            rateAssigned.Sort((x, y) => Math.Sign(x.Item2 - y.Item2));
            var validateArranged = rateAssigned.Where(x => x.Item2 < 1.0).ToList();
            var unsignedCount = rateUnassigned.Count;
            if (unsignedCount == 0)
                return validateArranged;

            var maxAssignedRate = validateArranged.Max(x => x.Item2);
            var leftRates = 1 - maxAssignedRate;
            var dr = unsignedCount == 1 ? leftRates : leftRates / (unsignedCount - 1);
            var r = leftRates;
            foreach (var tuple in rateUnassigned) {
                validateArranged.Add(Tuple.Create(tuple.Item1, r));
                r += dr;
            }
            return validateArranged;
        }
    }
}
